home *** CD-ROM | disk | FTP | other *** search
- -------------------------------------------------------------------------*)
-
- IMPLEMENTATION MODULE StorBase;
-
- (* Idee : Johannes Leckebusch, Peter Sollich *)
- (* Realisation : Peter Sollich *)
- (* Dynamic-Heap : Peter Hellinger *)
- (* Stand : 04.12.88 *)
-
- (*$V- *) (* Overflow-Checks *)
- (*$R- *) (* Range-Checks *)
- (*$S- *) (* Stack-Check *)
- (*$N- *) (* NIL-Checks *)
- (*$T- *) (* Range- und Overflow-Checks für TDI-Compiler vor 3.01 *)
- (*$Q+ *) (* Modul-Intern Branch-Befehle statt Jumps verwenden *)
-
- FROM SYSTEM IMPORT ADDRESS, NULL;
- FROM RTError IMPORT ErrorHalt;
- IMPORT GEMDOS;
-
- (*#######################################################################*)
-
- CONST lisp = FALSE;
-
- (* Falls Lispmap gewünscht, bitte dieses Flag auf TRUE sezten *)
-
- (*#######################################################################*)
-
- (*CONST cgrain = 16;*)
- CONST cSetGrain = LONGCARD(8); (* Granule-Setgröße *)
- cMinHeapSize = 64; (* Minimum-Heap *)
- cMaxHeapSize = 16777215; (* 16 Megabyte maximaler Heap *)
- cMaxGranules = 1048575; (* Maximale Anzahl der Granules *)
- cBytesInSet = 131071; (* Maximum Bytes im Set *)
- GEMReserve = 010000H; (* 64kb Restspeicher für GEM *)
-
-
- TYPE BlockPtr = POINTER TO Block; (* Zeiger auf ein Element des Heaps *)
- Block = RECORD
- bigger : BlockPtr; (* Zeiger auf größere Blöcke (rechts) *)
- equal : BlockPtr; (* Zeiger auf kleinere Blöcke (links) *)
- back : BlockPtr; (* Zeiger auf den vorhergehenden Block *)
- size : LONGCARD; (* Größe des Blocks *)
- END;
-
-
- TYPE ByteSet = SET OF [0..7]; (* Basistyp für das BitmapSet *)
- mapSet = ARRAY [0..cBytesInSet] OF ByteSet;
-
-
- VAR root : BlockPtr; (* Die Wurzel unseres Baumes *)
- initialBlock : BlockPtr; (* Erster Block des Baumes *)
- largeSentinel : BlockPtr; (* Lezter Block im Heap *)
- freeMap : POINTER TO mapSet;
- lispMap : POINTER TO mapSet;
- GranulesUsed : LONGCARD; (* Wird vorerst nicht mehr benutzt *)
- heapUsed : LONGCARD; (* Anzahl der benutzten Bytes *)
- heapStart : ADDRESS;
- heapSize : LONGCARD; (* Größe des Heap *)
- Dynamic : BOOLEAN; (* Flag für Dynamic-Option *)
- defaultSize : LONGCARD; (* Standardgröße für Heaperweiterung *)
- FreeMapSize : LONGCARD; (* Größe der Bitmap *)
- MaxHeapSize : LONGCARD; (* Maximale Größe des Heaps *)
- MemoryBottom : ADDRESS; (* Unteres Ende des Speichers *)
- PhysicalTop : ADDRESS; (* Oberes Ende des Speichers *)
-
-
-
- PROCEDURE AppendHeap (Amount: LONGCARD; mark: BOOLEAN): BOOLEAN;
- (* fügt neuen Block in den Heap ein, FALSE wenn nicht möglich *)
-
- VAR Block, b1: BlockPtr;
- adr: ADDRESS;
- lc: LONGCARD;
- VAR l,g: LONGCARD;
-
- BEGIN
-
- (* erst mal Testen ob soviel Speicher da ist *)
- GEMDOS.Alloc(0FFFFFFFFH,adr); lc:= LONGCARD(adr);
- IF (lc>GEMReserve) THEN
- DEC(lc,GEMReserve) (* Gemdos-Minimum reservieren *)
- ELSE
- Dynamic:= FALSE; (* Speicher kleiner als GEMReserve -> nix geht mehr *)
- RETURN FALSE;
- END;
-
- IF lc<Amount THEN
- Amount:=lc;
- Dynamic:=FALSE;
- (* Kein Speicher mehr zur Verfügung -> AppendHeap darf nicht mehr
- * aufgerufen werden, da sonst Restspeicher für GEM verbraten wird!
- *)
- END;
-
- (* Nur Vielfache von cgrain als Blockgröße zulassen *)
- INC(Amount,(cgrain-1)-(Amount+(cgrain-1)) MOD cgrain);
-
- (* Testen, ob Amount im gültigen Bereich *)
- IF (Amount<cMinHeapSize) OR (Amount>MaxHeapSize) THEN
- Dynamic:= FALSE; RETURN FALSE;
- END;
-
- (* Speicher abrufen *)
- GEMDOS.Alloc(Amount,Block);
- IF Block=NULL THEN
- Dynamic:=FALSE;
- RETURN FALSE
- END;
-
- INC(heapSize,Amount); (* neue Heapgröße berechnen *)
- largeSentinel^.size:= heapSize+1;
-
- (* Unseren neuen Block als von ALLOCATE behandelt tarnen *)
- (* 04.12.88: Wie hat das bloß jemals funktionieren können ??? *)
- Block^.size:= Amount-(cgrain * 2);
- b1:= (ADDRESS(Block)+ADDRESS(Block^.size))-ADDRESS(cgrain);
- b1^.size:= Block^.size;
- INC(heapUsed,Amount); (* Zur Tarnung *)
-
- (* Nun wird der Block noch in der Bitmap als Belegt gekennzeichnet.
- * Es genügt, das erste Bit zu setzen, da deallocate auch nur das
- * erste Block-Bit in der freeMap testet. Zeit ist Geld!
- *)
- l:= LONGCARD( ADDRESS(Block) - MemoryBottom) DIV cgrain;
- g:= Amount DIV cgrain;
- EXCL(freeMap^[l DIV cSetGrain],SHORT(l MOD cSetGrain));
- IF lisp AND mark THEN
- EXCL(lispMap^[l DIV cSetGrain],SHORT(l MOD cSetGrain));
- END;
-
- (* Und nun der Clou: wir schicken den Block durch DEALLOCATE !! *)
- dealloc(Block,Amount,mark);
-
- RETURN TRUE;
- END AppendHeap;
-
- PROCEDURE allocate (VAR Addr: ADDRESS; Amount: LONGCARD; mark: BOOLEAN);
- VAR Block,b : BlockPtr;
- b1,b2,b3: BlockPtr;
- l,g : LONGCARD;
- m : LONGCARD; (* für Testzwecke *)
- i : INTEGER; (* für createheap *)
- BEGIN
-
- Addr:= NIL; (* Na denn... *)
-
- (* Wenn nicht installiert, muß der Heap initialisiert werden *)
- IF root = NIL THEN
- IF (Amount>=defaultSize) THEN
- i:= createheap(Amount+defaultSize);
- ELSE
- i:= createheap(defaultSize);
- END;
- IF i<0 THEN RETURN; END;
- (* hier kann nur 0 oder -1 zurückkommen, da root=NIL *)
- END;
-
- IF (Amount>heapSize) THEN (* Grmpfft! Siehe Bugnote 25.11.88 *)
- IF Dynamic THEN
- IF NOT AppendHeap(Amount,mark) THEN RETURN END;
- ELSE
- RETURN;
- END;
- END;
-
- Block:= root; (* Laufzeiger auf Beginn des Heap-Baumes *)
-
- (* Nur Vielfache von cgrain als Blockgröße zulassen *)
- INC(Amount,(cgrain-1)-(Amount+(cgrain-1)) MOD cgrain);
-
- (* Suche nach einem Block größer oder gleich dem Angeforderten *)
- REPEAT Block:= Block^.bigger UNTIL Block^.size>Amount;
-
- IF Block^.size>heapSize THEN (* Heapoverflow !! *)
- IF Dynamic THEN
- IF NOT AppendHeap(defaultSize,mark) THEN RETURN END; (* nichts geht mehr *)
- allocate(Addr,Amount,mark);
- ELSE
- RETURN;
- END;
- RETURN;
- END;
-
- b1:= Block^.back; (* b1 = vorhergehender Block *)
-
- IF Block^.size=Amount THEN
- (* Block hat gleiche Größe wie angefordert, das ist einfach *)
-
- (*-- Block aus der Liste lösen und Liste restaurieren --*)
- b2:= Block^.equal;
- b3:= Block^.bigger;
- IF b2=NIL THEN
- b1^.bigger:= b3;
- b3^.back:= b1;
- ELSE
- b1^.bigger:= b2;
- b2^.bigger:= b3;
- b2^.back:= b1;
- b3^.back:= b2;
- END;
-
- ELSE (* Block ist größer als angefordert -> nu wirds kompliziert *)
-
- (* In Verbindung mit der dynamischen Erweiterungsmöglichkeit des Heaps
- * ergibt sich hier ein gar nicht so leicht aufzudeckender Fehler:
- *
- * Der allozierte Speicher wird am OBEREN Ende des gefundenen Blocks
- * abgezweigt. Hierdurch entsteht der Effekt, daß die Daten in UMGE-
- * kehrter Reihenfolge im Heap stehen - also die zuerst abgelegten Daten
- * auf höheren Adressen als die zuletzt abgelegten. Der Heap wächst
- * gewissermaßen "nach unten".
- *
- * Wird nun mittels AppendHeap ein neuer Block in den Heap integriert,
- * wird er in aller Regel eine höhere Adresse als der bereits bestehende
- * Heap haben, also im Speicher weiter "hinten" liegen.
- *
- * Da der oberste Block des bereits bestehenden Heaps auch in aller Regel
- * belegt sein wird (er wird ja schließlich als erster alloziert) kann
- * deallocate den neuen Block nicht mit dem Rest des bestehenden Heaps
- * verschmelzen - der Rest steht ja am BEGINN des Blocks, nicht am Ende
- * wie es notwendig wäre.
- *
- * So können Blöcke entstehen, die nicht mehr durchs Programm allozierbar
- * sind, da sie einfach zu klein sind. Je nachdem, wie die durchschnittliche
- * Blockgröße aussieht, kann so ein Rest zwischen 1 und 100 Kilobyte
- * entstehen (bei einem freien Speicher von ca 3.5 Mb).
- *
- * Ich habe versucht diesen Fehler auszumerzen, indem ich die Allozierungs-
- * reihenfolge geändert habe. Der Rest-Heap sollte nun am Ende des Blocks
- * stehen und sich mit dem neuen Block verschmelzen lassen.
- *
- * Hp 25.12.88
- *)
-
- Addr:= Block; (* die halbe Miete hätten wir... *)
-
- (*-- Block aus Liste nehmen und Liste restaurieren --*)
- b2:= Block^.equal;
- b3:= Block^.bigger;
- IF b2 = NIL THEN
- b1^.bigger:= b3;
- b3^.back:= b1;
- ELSE
- b1^.bigger:= b2;
- b2^.bigger:= b3;
- b2^.back:= b1;
- b3^.back:= b2;
- END;
-
- (* Block-Pointer "nach oben" verschieben *)
- b:= ADDRESS(Block) + ADDRESS(Amount);
- b^.bigger:= Block^.bigger;
- b^.equal := Block^.equal;
- b^.back := Block^.back;
- b^.size := Block^.size - Amount;
- Block:= b;
-
- (* Nun suchen wir ein trautes Plätzchen für den Rest unseres Blocks *)
-
- b2:= root;
- REPEAT b2:= b2^.bigger UNTIL b2^.size>=Block^.size;
- (* b2 zeigt auf einen Block größer oder gleich unseres Blockrestes *)
-
- (* Block an neuer Stelle einfügen *)
- b1:= b2^.back;
- b1^.bigger:= Block;
- Block^.back:= b1;
- b2^.back:= Block;
- IF b2^.size>Block^.size THEN
- (* Block zwischen b1 und b2 einfügen *)
- Block^.bigger:= b2;
- Block^.equal := NIL;
- ELSE
- (* Block nach b2 einfügen *)
- b3:= b2^.bigger;
- Block^.bigger:= b3;
- Block^.equal:= b2;
- b3^.back:= Block;
- END;
-
- (* oberes Ende des Blocks berechnen *)
- b2:= (ADDRESS(Block) + ADDRESS(Block^.size)) - ADDRESS(cgrain);
- b2^.size:= Block^.size;
- END (* IF Block^.Amount = Amount *);
-
- (* Nun wird der Block noch in der Bitmap als Belegt gekennzeichnet *)
- l:= LONGCARD(Addr-MemoryBottom) DIV cgrain;
- g:= Amount DIV cgrain;
- INC(heapUsed,Amount);
- REPEAT
- EXCL(freeMap^[l DIV cSetGrain],SHORT(l MOD cSetGrain));
- IF lisp AND mark THEN
- EXCL(lispMap^[l DIV cSetGrain],SHORT(l MOD cSetGrain));
- END;
- INC(l); DEC(g);
- UNTIL g=0;
-
- (* Uff... *)
- END allocate;
-
-
- PROCEDURE deallocrest (VAR Addr: ADDRESS; Amount: LONGCARD;
- VAR new: LONGCARD; mark: BOOLEAN);
- VAR newAddr : ADDRESS;
- newAmount : LONGCARD;
- BEGIN
- IF Addr # NIL THEN
- INC (Amount, (cgrain-1) - (Amount+ (cgrain-1)) MOD cgrain);
- INC (new, (cgrain-1) - (new + (cgrain-1)) MOD cgrain);
- IF (new > Amount) THEN
- dealloc (Addr, Amount, mark);
- ELSE
- newAddr:= Addr + ADDRESS (new);
- newAmount:= Amount - new;
- dealloc (newAddr, newAmount, mark);
- END;
- END;
- END deallocrest;
-
-
- PROCEDURE dealloc (VAR Addr: ADDRESS; Amount: LONGCARD; mark: BOOLEAN);
- VAR s,b,b1,b2,b3 : BlockPtr;
- l,r,g : LONGCARD;
- BEGIN
-
- IF Addr=NIL THEN RETURN END; (* gibts sonst Bömbchen *)
-
- (* Nur Vielfaches von cgrain als Größe zulassen *)
- INC(Amount,(cgrain-1) - (Amount+(cgrain-1)) MOD cgrain);
-
- (* Schutz vor Doppelten Pointern *)
- l:= LONGCARD((Addr-MemoryBottom) DIV cgrain);
- IF (SHORT(l MOD cSetGrain) IN ByteSet(freeMap^[l DIV cSetGrain]))
- THEN
- Addr:= NIL;
- ErrorHalt ('STORBASE| double pointer deallocation');
- RETURN;
- END;
-
- (* Block in der Bitmap als frei kennzeichnen *)
- (* l:= LONGCARD ((Addr-MemoryBottom) DIV cgrain); Ist hier überflüssig *)
- g:= Amount DIV cgrain;
- DEC(heapUsed,Amount);
- r:= l;
- REPEAT
- INCL(freeMap^[r DIV cSetGrain],SHORT(r MOD cSetGrain));
-
- IF lisp AND mark THEN
- INCL(lispMap^[r DIV cSetGrain],SHORT(r MOD cSetGrain));
- END;
-
- INC(r); DEC(g)
- UNTIL g=0;
-
- s:= root; (* Start des Heap *)
- b:= Addr; (* Adresse des Blocks *)
-
- (* physikalisch Rechten Nachbar in der Bitmap auf Frei testen *)
- IF SHORT(r MOD cSetGrain) IN ByteSet(freeMap^[r DIV cSetGrain]) THEN
-
- b:= ADDRESS(b) + ADDRESS(Amount); (* Adresse des Blocks berechnen *)
- INC (Amount, b^.size); (* Blockgröße zu der Unseren addieren *)
-
- (* Die Zeiger der beiden Blöcke verküpfen *)
- b1:= b^.back; b2:= b^.equal;
- IF b1^.size=b^.size THEN
- b1^.equal:= b2;
- IF b2#NIL THEN b2^.back:= b1 END;
- ELSE
- b3:= b^.bigger; s:= b3;
- IF b2 = NIL THEN
- b1^.bigger:= b3; b3^.back:= b1;
- ELSE
- b1^.bigger:= b2; b2^.bigger:= b3; b2^.back:= b1; b3^.back:= b2;
- END;
- END;
- b:= Addr;
-
- END; (* IF SHORT *)
-
- (* physikalisch Linken Nachbar in der Bitmap auf Frei testen *)
- IF SHORT((l-1) MOD cSetGrain) IN ByteSet(freeMap^[(l-1) DIV cSetGrain]) THEN
- b1:= ADDRESS(b) - cgrain;
-
- (* In allocate haben wir auf die letzten 4 Bytes die Größe des Blocks
- * eingetragen. Die holen wir uns nun mittels b1... *)
- b:= Addr - ADDRESS(b1^.size); (* Startadresse des linken Blocks *)
-
- INC(Amount,b^.size);
- b1:=b^.back; b2:= b^.equal;
-
- IF b1^.size=b^.size THEN
- b1^.equal:= b2;
- IF b2#NIL THEN b2^.back:= b1 END;
- ELSE
- b3:= b^.bigger;
- IF s^.size<b3^.size THEN s:= b3 END;
- IF b2=NIL THEN
- b1^.bigger:= b3; b3^.back:= b1;
- ELSE
- b1^.bigger:= b2; b2^.bigger:= b3; b2^.back:= b1; b3^.back:= b2;
- END (* IF b2=NIL *);
- END (* IF b1^.Amount *);
-
- END (* IF l - 1 *);
-
- b^.size:= Amount; b1:= ADDRESS(b)+ADDRESS(Amount)-cgrain;
- b1^.size:= Amount; b2:= s;
- WHILE b2^.size<Amount DO b2:=b2^.bigger END;
- b1:= b2^.back; b1^.bigger:= b; b^.back:= b1; b2^.back:= b;
- IF b2^.size>Amount THEN (* insert b between b1 and b2 *)
- b^.bigger:= b2; b^.equal:= NIL;
- ELSE (* insert b above b2 *)
- b3:= b2^.bigger; b^.bigger:= b3; b^.equal:= b2; b3^.back:= b;
- END (* IF b2^.size *);
-
- Addr:= NIL; (* Schwitz... *)
-
- END dealloc;
-
-
-
- PROCEDURE createheap (Amount: LONGCARD): INTEGER;
- VAR smallSentinel: BlockPtr;
- i,l,g : LONGCARD;
- a : ADDRESS; (*21.12.88 Hp*)
- BEGIN
-
- (* Fehler, wenn Heap schon existiert *)
- IF root#NIL THEN RETURN -2 END;
-
- (* Mal sehen was so im Speicher rumliegt *)
- GEMDOS.Alloc(0FFFFFFFFH,a);
- l:=LONGCARD(a); DEC(l,GEMReserve);
-
- (* Bereich testen und Heapsize korrigieren *)
- INC(Amount,(cgrain-1)-(Amount+(cgrain-1)) MOD cgrain);
- IF l<Amount THEN Amount:=l; END;
- IF (Amount<cMinHeapSize) OR (Amount>l) THEN RETURN -1; END;
-
- (* Speicher anfordern *)
- GEMDOS.Alloc(Amount,heapStart);
-
- heapSize:= Amount;
-
- smallSentinel:= heapStart; (* unteres Ende des Heaps *)
- largeSentinel:= heapStart+cgrain; (* Zeiger auf obere Ende des Heap *)
- initialBlock := heapStart+cgrain*2; (* Erster Block des Heap *)
-
- (* "kleinen Wächter" initalisieren *)
- WITH smallSentinel^ DO
- bigger:= initialBlock;
- equal := NIL;
- back := NIL;
- size := 0;
- END;
-
- (* "großen Wächter" initialisieren *)
- WITH largeSentinel^ DO
- bigger:= NIL;
- equal := NIL;
- back := initialBlock;
- size := heapSize+1;
- (* Aktuelle Heapgröße +1. So wird in allocate das Ende des Heaps erkannt. *)
- END;
-
- (* Ersten Block intialisieren *)
- WITH initialBlock^ DO
- bigger:= largeSentinel;
- equal := NIL;
- back := smallSentinel;
- size := Amount-(cgrain * 2);
- DEC(size,size MOD cgrain);
- END;
-
- heapUsed:= cgrain * 2;
-
- root:= smallSentinel;
-
- (* Heap in der Bitmap als frei kennzeichnen *)
- l:= LONGCARD( (heapStart + ADDRESS(cgrain * 2)) - MemoryBottom) DIV cgrain;
- g:= Amount DIV cgrain;
- REPEAT
- INCL(freeMap^[l DIV cSetGrain],SHORT(l MOD cSetGrain));
-
- IF lisp THEN
- INCL(lispMap^[l DIV cSetGrain],SHORT(l MOD cSetGrain));
- END;
-
- INC(l); DEC(g);
- UNTIL g=0;
-
- (* Kennzeichnet unteres Ende des Heap *)
- EXCL(freeMap^[0],1);
-
- IF lisp THEN EXCL(lispMap^[0],1); END;
-
- RETURN 0;
- END createheap;
-
-
- PROCEDURE free(): LONGCARD;
- BEGIN
- RETURN heapSize - heapUsed;
- END free;
-
-
- PROCEDURE heapbase (): ADDRESS;
- BEGIN
- RETURN initialBlock;
- END heapbase;
-
-
- PROCEDURE granulemarked (addr: ADDRESS): BOOLEAN;
- VAR l: LONGCARD;
- BEGIN
- IF lisp THEN
- l:= LONGCARD((addr-MemoryBottom) DIV cgrain);
- RETURN
- ~(SHORT((l-1) MOD cSetGrain) IN ByteSet(lispMap^[(l-1) DIV cSetGrain]));
- ELSE
- RETURN FALSE;
- END;
- END granulemarked;
-
-
- PROCEDURE usedgran (mark: BOOLEAN): LONGCARD;
- BEGIN
- (* Da sollte wohl noch was kommen... ?!? *)
- RETURN heapUsed DIV cgrain;
- END usedgran;
-
-
- PROCEDURE dynamic (dyn: BOOLEAN);
- BEGIN
- Dynamic:= dyn;
- END dynamic;
-
-
- PROCEDURE setDefaultSize (size: LONGCARD);
- BEGIN
- defaultSize:= size;
- END setDefaultSize;
-
-
- PROCEDURE memAvail(): LONGCARD;
- VAR a: ADDRESS;
- l: LONGCARD;
- BEGIN
- GEMDOS.Alloc(0FFFFFFFFH,a);
- RETURN (heapSize + LONGCARD(a)) - (heapUsed + GEMReserve);
- END memAvail;
-
-
- VAR c : LONGCARD;
- a : ADDRESS;
- x : POINTER TO LONGCARD;
- y : POINTER TO CHAR;
- phystop[042EH]: ADDRESS; (* Systemvariable *)
- membot[0432H] : ADDRESS; (* Systemvariable *)
-
- BEGIN
-
- Dynamic := TRUE; (* Dynamic-Option gewählt *)
- defaultSize := 010000H; (* 64Kb Default Heapsize *)
- GranulesUsed := 0; (* Noch kein Granule gebraucht *)
- heapUsed := 0; (* Noch kein Byte belegt *)
- root := NIL; (* Heap ist leer *)
-
- (* maximale Speichergröße feststellen *)
- a:=0; GEMDOS.Super(a);
- PhysicalTop := phystop;
- MemoryBottom := membot;
- GEMDOS.Super(a);
-
- (* Maximale Größe des freien Speichers *)
- MaxHeapSize:= LONGCARD( PhysicalTop - MemoryBottom);
-
- (* Größe der Bitmap berechnen *)
- FreeMapSize:= MaxHeapSize DIV (cgrain * cSetGrain);
- INC(FreeMapSize);
-
- (* Speicher anfordern *)
- GEMDOS.Alloc(FreeMapSize,freeMap);
- IF (freeMap=NULL) THEN ErrorHalt('STORBASE| Freemap not generated'); END;
- (* Hier könnte man noch ne Msg. bringen *)
-
- (* Bitmap löschen. Geht so schneller *)
- x:= ADDRESS(freeMap);
- FOR c:=0 TO (FreeMapSize DIV 4) DO x^:=0; (*INC(x,4);*)
- INC (x); INC (x); INC (x); INC (x);
- END;
- y:= ADDRESS(x);
- FOR c:=0 TO (FreeMapSize MOD 4) DO y^:=0C; INC(y); END;
-
- (* Lispmap initialisieren *)
- IF lisp THEN
- GEMDOS.Alloc(FreeMapSize,lispMap);
- IF (lispMap=NULL) THEN ErrorHalt('STORBASE| Lispmap not generated'); END;
- x:= ADDRESS(lispMap);
- FOR c:=0 TO (FreeMapSize DIV 4) DO x^:=0; INC(x,4); END;
- y:= ADDRESS(x);
- FOR c:=0 TO (FreeMapSize MOD 4) DO y^:=0C; INC(y); END;
- END;
-
- END StorBase.
-
- ə